using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;

namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        // Keyboard states used to determine key presses
        KeyboardState currentKeyboardState;
        KeyboardState previousKeyboardState;

        Random random; 
        Texture2D defaultTexture;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Rectangle gameWindow;

        Camera2D camera;
        List<Object2D> sampleObjects;
        
        public Game1()
        {
            this.graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }


        protected override void Initialize()
        {
            // set resolution
            //InitGraphicsMode(320, 240, false);

            this.random = new Random();
            this.defaultTexture = Content.Load<Texture2D>("default");
            this.gameWindow = GraphicsDevice.Viewport.TitleSafeArea;

            this.camera = new Camera2D();
            // Sample objects
            sampleObjects = new List<Object2D>();
            for (int i = 0; i < 100; i++)
                sampleObjects.Add(new Object2D());

            base.Initialize();
        }


        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            this.camera.Initialize(new Vector2(this.gameWindow.Width/2, this.gameWindow.Height/2));
            foreach (var obj in this.sampleObjects)
            {
                Vector2 position = new Vector2((float)this.random.NextDouble() * (this.gameWindow.Width), (float)this.random.NextDouble() * (this.gameWindow.Height));
                obj.Initialize(LoadTexture("default2"), position);
                obj.Scale = 0.2f;
            }

        }


        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        protected override void Update(GameTime gameTime)
        {
            // Save the previous state of the keyboard so we can determine single key presses
            this.previousKeyboardState = this.currentKeyboardState;

            // Read the current state of the keyboard and store it
            this.currentKeyboardState = Keyboard.GetState();

            // Pass keyboard state to things that need it...
            if (this.currentKeyboardState.IsKeyDown(Keys.Escape))
                this.Exit();

            // Camera controls
            if (this.currentKeyboardState.IsKeyDown(Keys.Up))
                this.camera.MoveWorld(new Vector2(0, -1)*5);
            if (this.currentKeyboardState.IsKeyDown(Keys.Left))
                this.camera.MoveWorld(new Vector2(-1, 0) * 5);
            if (this.currentKeyboardState.IsKeyDown(Keys.Right))
                this.camera.MoveWorld(new Vector2(1, 0) * 5);
            if (this.currentKeyboardState.IsKeyDown(Keys.Down))
                this.camera.MoveWorld(new Vector2(0, 1) * 5);
            if (this.currentKeyboardState.IsKeyDown(Keys.A))
                this.camera.Rotation += 0.01f;
            if (this.currentKeyboardState.IsKeyDown(Keys.D))
                this.camera.Rotation -= 0.01f;
            if (this.currentKeyboardState.IsKeyDown(Keys.W))
                this.camera.Zoom += 0.01f;
            if (this.currentKeyboardState.IsKeyDown(Keys.S))
                this.camera.Zoom -= 0.01f;

            // TODO: Add your update logic here
            int i = 0;
            foreach (var sampleObject in sampleObjects)
            {
                if (!this.gameWindow.Intersects(sampleObject.BoundingBox))
                    sampleObject.Position.Y = 0;

                sampleObject.RotationAngle += Common.ElapsedSeconds(gameTime) + (i * 0.001f);
                sampleObject.RotationAngle %= (2f * MathHelper.Pi);
                sampleObject.Position.X += ((((float)this.random.NextDouble()) * 2f) - 1f);
                sampleObject.Position.Y += (((float)this.random.NextDouble()) + i * 0.1f);
                sampleObject.Scale += ((((float)this.random.NextDouble()) * 2f) - 1f) / 50f;
                sampleObject.Opacity = (byte)MathHelper.Clamp((float)((double)sampleObject.Opacity + Math.Floor(((this.random.NextDouble() * 2) - 1) * 8)), 32f, 255f);
                i++;
            }
            

            base.Update(gameTime);
        }


        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            this.spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null,
                                    this.camera.get_transformation(GraphicsDevice));
            // Draw items affected by camera (game objects)

            foreach (var sampleObject in sampleObjects)
            {
                sampleObject.Draw(this.spriteBatch);
            }


            this.spriteBatch.End();
            this.spriteBatch.Begin();
            // Draw items not affected by camera (GUI)



            this.spriteBatch.End();
            base.Draw(gameTime);
        }

        private Texture2D LoadTexture(string assetName)
        {
            try { return Content.Load<Texture2D>(assetName); }
            catch { return this.defaultTexture; }
        }

        /// <summary>
        /// Attempt to set the display mode to the desired resolution.  Itterates through the display
        /// capabilities of the default graphics adapter to determine if the graphics adapter supports the
        /// requested resolution.  If so, the resolution is set and the function returns true.  If not,
        /// no change is made and the function returns false.
        /// </summary>
        /// <param name="iWidth">Desired screen width.</param>
        /// <param name="iHeight">Desired screen height.</param>
        /// <param name="bFullScreen">True if you wish to go to Full Screen, false for Windowed Mode.</param>
        private bool InitGraphicsMode(int iWidth, int iHeight, bool bFullScreen)
        {
            // If we aren't using a full screen mode, the height and width of the window can
            // be set to anything equal to or smaller than the actual screen size.
            if (bFullScreen == false)
            {
                if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
                    && (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
                {
                    graphics.PreferredBackBufferWidth = iWidth;
                    graphics.PreferredBackBufferHeight = iHeight;
                    graphics.IsFullScreen = bFullScreen;
                    graphics.ApplyChanges();
                    return true;
                }
            }
            else
            {
                // If we are using full screen mode, we should check to make sure that the display
                // adapter can handle the video mode we are trying to set.  To do this, we will
                // iterate thorugh the display modes supported by the adapter and check them against
                // the mode we want to set.
                foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                {
                    // Check the width and height of each mode against the passed values
                    if ((dm.Width == iWidth) && (dm.Height == iHeight))
                    {
                        // The mode is supported, so set the buffer formats, apply changes and return
                        graphics.PreferredBackBufferWidth = iWidth;
                        graphics.PreferredBackBufferHeight = iHeight;
                        graphics.IsFullScreen = bFullScreen;
                        graphics.ApplyChanges();
                        return true;
                    }
                }
            }
            return false;
        }

    }
}
